// BigInt.js - Arbitrary size integer math package for JavaScript
// Copyright (C) 2000 Masanao Izumo <iz@onicos.co.jp>
// Version: 1.0.1
// Licence: GPL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
//
// Interfaces:
// x = new BigInt("1234567890123456789012345678901234567890");
// y = new BigInt("0x123456789abcdef0123456789abcdef0");
// z = x.clone();
// z = bigint_uminus(x);
// z = bigint_plus(x, y);
// z = bigint_minus(x, y);
// z = bigint_mul(x, y);
// z = bigint_div(x, y);
// z = bigint_mod(x, y);
// cmp = bigint_cmp(x, y);	/* return -1, 0, or 1 */
// num = bigint_number(x);	/* convert normal number (may be floating) */
//

function _BigInt_toString()
{
    return this.toStringBase(10);
}
function _BigInt_toStringBase(base)
{
    var i, j, hbase;
    var t;
    var ds;
    var c;

    i = this.len;
    if(i == 0)
      return "0";
    if(i == 1 && !this.digits[0])
      return "0";

    switch(base) {
      default:
      case 10:
      j = Math.floor((2*8*i*241)/800)+2;
      hbase = 10000;
      break;

      case 16:
      j = Math.floor((2*8*i)/4)+2;
      hbase = 0x10000;
      break;

      case 8:
      j = (2*8*i)+2;
      hbase = 010000;
      break;

      case 2:
      j = (2*8*i)+2;
      hbase = 020;
      break;
    }

    t = this.clone();
    ds = t.digits;
    s = "";

    while (i && j) {
      var k = i;
      var num = 0;

      while (k--) {
	num = (num<<16) + ds[k];
	if(num < 0) num += 4294967296;
	ds[k] = Math.floor(num / hbase);
	num %= hbase;
      }

      if (ds[i-1] == 0)
        i--;
      k = 4;
      while (k--) {
	c = (num % base);
	s = "0123456789abcdef".charAt(c) + s;
	--j;
	num = Math.floor(num / base);
	if (i == 0 && num == 0) {
	  break;
	}
      }
    }

    i = 0;
    while(i < s.length && s.charAt(i) == "0")
      i++;
    if(i)
      s = s.substring(i, s.length);
    if(!this.sign)
      s = "-" + s;
    return s;
}
function _BigInt_clone()
{
  var x, i;

  x = new BigInt(this.len, this.sign);
  for(i = 0; i < this.len; i++)
    x.digits[i] = this.digits[i];
  return x;
}

function BigInt(len, sign)
{
  var i, x, need_init;

  // Setup member functions.
  // Note: There is G.C. bug of function() in Netscape!
  //       Don't use anonymous function.
  this.toString = _BigInt_toString;
  this.toStringBase = _BigInt_toStringBase;
  this.clone = _BigInt_clone;

  if(BigInt.arguments.length == 0) {
    this.sign = true;
    this.len = len = 1;
    this.digits = new Array(1);
    need_init = true;
  } else if(BigInt.arguments.length == 1) {
    x = bigint_from_any(BigInt.arguments[0]);
    if(x == BigInt.arguments[0])
      x = x.clone();
    this.sign = x.sign;
    this.len = x.len;
    this.digits = x.digits;
    need_init = false;
  } else {
    this.sign = (sign ? true : false);
    this.len = len;
    this.digits = new Array(len);
    need_init = true;
  }

  if(need_init) {
    for(i = 0; i < len; i++)
      this.digits[i] = 0;
  }
}

function bigint_norm(x)
{
  var len = x.len;
  var ds = x.digits;

  while(len-- && !ds[len])
    ;
  x.len = ++len;
  return x;
}

function bigint_from_int(n)
{
  var sign, big, i;

  if(n < 0) {
    n = -n;
    sign = false;
  } else
    sign = true;
  n &= 0x7fffffff;

  if(n <= 0xffff) {
    big = new BigInt(1, 1);
    big.digits[0] = n;
  } else {
    big = new BigInt(2, 1);
    big.digits[0] = (n & 0xffff);
    big.digits[1] = ((n>>16) & 0xffff);
  }
  return big;
}

function bigint_from_string(str, base)
{
  var str_i;
  var sign = true;
  var c;
  var len;
  var z;
  var zds;
  var num;
  var i;
  var blen = 1;

  str += "@";	// Terminator;

  str_i = 0;
  // TODO: skip white spaces

  if(str.charAt(str_i) == "+") {
    str_i++;
  }
  else if (str.charAt(str_i) == "-") {
    str_i++;
    sign = false;
  }

  if (str.charAt(str_i) == "@")
    return null;

  if (!base) {
    if (str.charAt(str_i) == "0") {
      c = str.charAt(str_i + 1);
      if (c == "x" || c == "X") {
	base = 16;
      }
      else if (c == "b" || c == "B") {
	base = 2;
      }
      else {
	base = 8;
      }
    }
    else {
      base = 10;
    }
  }

  if (base == 8) {
    while (str.charAt(str_i) == "0")
      str_i++;
    len = 3 * (str.length - str_i);
  }
  else {			// base == 10, 2 or 16
    if (base == 16 && str.charAt(str_i) == '0' && (str.charAt(str_i+1) == "x" || str.charAt(str_i+1) == "X")) {
      str_i += 2;
    }
    if (base == 2 && str.charAt(str_i) == '0' && (str.charAt(str_i+1) == "b"||str.charAt(str_i+1) == "B")) {
      str_i += 2;
    }
    while (str.charAt(str_i) == "0")
      str_i++;
    if (str.charAt(str_i) == "@") str_i--;
    len = 4 * (str.length - str_i);
  }

  len = (len>>4)+1;
  z = new BigInt(len, sign);
  zds = z.digits;

  while(true) {
    c = str.charAt(str_i++);
    if(c == "@")
      break;
    switch (c) {
    case '0': c = 0; break;
    case '1': c = 1; break;
    case '2': c = 2; break;
    case '3': c = 3; break;
    case '4': c = 4; break;
    case '5': c = 5; break;
    case '6': c = 6; break;
    case '7': c = 7; break;
    case '8': c = 8; break;
    case '9': c = 9; break;
    case 'a': case 'A': c = 10; break;
    case 'b': case 'B': c = 11; break;
    case 'c': case 'C': c = 12; break;
    case 'd': case 'D': c = 13; break;
    case 'e': case 'E': c = 14; break;
    case 'f': case 'F': c = 15; break;
    default:
      c = base;
      break;
    }
    if (c >= base)
      break;

    i = 0;
    num = c;
    while(true) {
      while (i<blen) {
	num += zds[i]*base;
	zds[i++] = (num & 0xffff);
	num >>>= 16;
      }
      if (num) {
	blen++;
	continue;
      }
      break;
    }
  }
  return bigint_norm(z);
}

function bigint_from_any(x)
{
  if(typeof(x) == "object") {
    if(x.constructor == BigInt)
      return x;
    return BigInt(1, 1);
  }

  if(typeof(x) == "string") {
    return bigint_from_string(x);
  }

  if(typeof(x) == "number") {
    var i, x1, x2, fpt, np;

    if(-2147483647 <= x && x <= 2147483647) {
      return bigint_from_int(x);
    }
    x = x + "";
    i = x.indexOf("e", 0);
    if(i == -1)
      return bigint_from_string(x);
    x1 = x.substr(0, i);
    x2 = x.substr(i+2, x.length - (i+2));

    fpt = x1.indexOf(".", 0);
    if(fpt != -1) {
      np = x1.length - (fpt+1);
      x1 = x1.substr(0, fpt) + x1.substr(fpt+1, np);
      x2 = parseInt(x2) - np;
    } else {
      x2 = parseInt(x2);
    }
    while(x2-- > 0) {
      x1 += "0";
    }
    return bigint_from_string(x1);
  }
  return BigInt(1, 1);
}

function bigint_uminus(x)
{
  var z = x.clone();
  z.sign = !z.sign;
  return bigint_norm(z);
}

function bigint_add_internal(x, y, sign)
{
  var z;
  var num;
  var i, len;

  sign = (sign == y.sign);
  if (x.sign != sign) {
    if (sign)
      return bigint_sub_internal(y, x);
    return bigint_sub_internal(x, y);
  }

  if (x.len > y.len) {
    len = x.len + 1;
    z = x; x = y; y = z;
  } else {
    len = y.len + 1;
  }
  z = new BigInt(len, sign);

  len = x.len;
  for (i = 0, num = 0; i < len; i++) {
    num += x.digits[i] + y.digits[i];
    z.digits[i] = (num & 0xffff);
    num >>>= 16;
  }
  len = y.len;
  while (num && i < len) {
    num += y.digits[i];
    z.digits[i++] = (num & 0xffff);
    num >>>= 16;
  }
  while (i < len) {
    z.digits[i] = y.digits[i];
    i++;
  }
  z.digits[i] = (num & 0xffff);
  return bigint_norm(z);
  //  return z;
}

function bigint_sub_internal(x, y)
{
  var z = 0;
  var zds;
  var num;
  var i;

  i = x.len;
  // if x is larger than y, swap
  if (x.len < y.len) {
    z = x; x = y; y = z;	// swap x y
  }
  else if (x.len == y.len) {
    while (i > 0) {
      i--;
      if (x.digits[i] > y.digits[i]) {
	break;
      }
      if (x.digits[i] < y.digits[i]) {
	z = x; x = y; y = z;	// swap x y
	break;
      }
    }
  }

  z = new BigInt(x.len, (z == 0) ? 1 : 0);
  zds = z.digits;

  for (i = 0, num = 0; i < y.len; i++) { 
    num += x.digits[i] - y.digits[i];
    zds[i] = (num & 0xffff);
    num >>>= 16;
  } 
  while (num && i < x.len) {
    num += x.digits[i];
    zds[i++] = (num & 0xffff);
    num >>>= 16;
  }
  while (i < x.len) {
    zds[i] = x.digits[i];
    i++;
  }
    
  return bigint_norm(z);
}

function bigint_plus(x, y)
{
  x = bigint_from_any(x);
  y = bigint_from_any(y);
  return bigint_add_internal(x, y, 1);
}

function bigint_minus(x, y)
{
  x = bigint_from_any(x);
  y = bigint_from_any(y);
  return bigint_add_internal(x, y, 0);
}

function bigint_mul(x, y)
{
  var i, j;
  var n = 0;
  var z;
  var zds, xds, yds;
  var dd, ee;
  var ylen;

  x = bigint_from_any(x);
  y = bigint_from_any(y);

  j = x.len + y.len + 1;
  z = new BigInt(j, x.sign == y.sign);

  xds = x.digits;
  yds = y.digits;
  zds = z.digits;
  ylen = y.len;
  while (j--)
    zds[j] = 0;
  for (i = 0; i < x.len; i++) {
    dd = xds[i]; 
    if (dd == 0)
      continue;
    n = 0;
    for (j = 0; j < ylen; j++) {
      ee = n + dd * yds[j];
      n = zds[i + j] + ee;
      if (ee)
	zds[i + j] = (n & 0xffff);
      n >>>= 16;
    }
    if (n) {
      zds[i + j] = n;
    }
  }

  return bigint_norm(z);
}

function bigint_divmod(x, y, modulo)
{
  var nx = x.len;
  var ny = y.len;
  var i, j;
  var yy, z;
  var xds, yds, zds, tds;
  var t2;
  var num;
  var dd, q;
  var ee;
  var mod, div;

  yds = y.digits;
  if (ny == 0 && yds[0] == 0)
    return null;	// Division by zero

  if (nx < ny || nx == ny && x.digits[nx - 1] < y.digits[ny - 1]) {
    if (modulo)
      return bigint_norm(x);
    return BigInt(1, 1);
  }

  xds = x.digits;
  if (ny == 1) {
    dd = yds[0];
    z = x.clone();
    zds = z.digits;
    t2 = 0;
    i = nx;
    while (i--) {
      t2 = t2 * 65536 + zds[i];
      zds[i] = (t2 / dd) & 0xffff;
      t2 %= dd;
    }
    z.sign = (x.sign == y.sign);
    if (modulo) {
      if (!x.sign)
	t2 = -t2;
      if (x.sign != y.sign) {
	t2 = t2 + yds[0] * (y.sign ? 1 : -1);
      }
      return bigint_from_int(t2);
    }
    return bigint_norm(z);
  }

  z = new BigInt(nx == ny ? nx + 2 : nx + 1,
		 x.sign == y.sign);
  zds = z.digits;
  if (nx == ny)
    zds[nx + 1] = 0;
  while (!yds[ny - 1])
    ny--;
  if ((dd = ((65536/(yds[ny-1]+1)) & 0xffff)) != 1) {
    yy = y.clone();
    tds = yy.digits;
    j = 0;
    num = 0;
    while (j<ny) {
      num += yds[j]*dd;
      tds[j++] = num & 0xffff;
      num >>= 16;
    }
    yds = tds;
    j = 0;
    num = 0;
    while (j<nx) {
      num += xds[j] * dd;
      zds[j++] = num & 0xffff;
      num >>= 16;
    }
    zds[j] = num & 0xffff;
  }
  else {
    zds[nx] = 0;
    j = nx;
    while (j--) zds[j] = xds[j];
  }
  j = nx==ny?nx+1:nx;
  do {
    if (zds[j] ==  yds[ny-1]) q = 65535;
    else q = ((zds[j]*65536 + zds[j-1])/yds[ny-1]) & 0xffff;
    if (q) {
      i = 0; num = 0; t2 = 0;
      do {			// multiply and subtract
	t2 += yds[i] * q;
	ee = num - (t2 & 0xffff);
	num = zds[j - ny + i] + ee;
	if (ee) zds[j - ny + i] = num & 0xffff;
	num >>= 16;
	t2 >>>= 16;
      } while (++i < ny);
      num += zds[j - ny + i] - t2; // borrow from high digit; don't update
      while (num) {		// "add back" required
	i = 0; num = 0; q--;
	do {
	  ee = num + yds[i];
	  num = zds[j - ny + i] + ee;
	  if (ee) zds[j - ny + i] = num & 0xffff;
	  num >>= 16;
	} while (++i < ny);
	num--;
      }
    }
    zds[j] = q;
  } while (--j >= ny);

  if (modulo) {			// just normalize remainder
    mod = z.clone();
    if (dd) {
      zds = mod.digits;
      t2 = 0; i = ny;
      while (i--) {
	t2 = (t2*65536) + zds[i];
	zds[i] = (t2 / dd) & 0xffff;
	t2 %= dd;
      }
    }
    mod.len = ny;
    mod.sign = x.sign;
    if (x.sign != y.sign) {
      return bigint_add_internal(mod, y, 1);
    }
    return bigint_norm(mod);
  }

  div = z.clone();
  zds = div.digits;
  j = (nx==ny ? nx+2 : nx+1) - ny;
  for (i = 0;i < j;i++) zds[i] = zds[i+ny];
  div.len = i;
  return bigint_norm(div);
}

function bigint_div(x, y)
{
  x = bigint_from_any(x);
  y = bigint_from_any(y);
  return bigint_divmod(x, y, 0);
}

function bigint_mod(x, y)
{
  x = bigint_from_any(x);
  y = bigint_from_any(y);
  return bigint_divmod(x, y, 1);
}

function bigint_cmp(x, y)
{
  var xlen;

  if(x == y)
    return 0;	// Same object

  x = bigint_from_any(x);
  y = bigint_from_any(y);
  xlen = x.len;

  if(x.sign != y.sign) {
    if(x.sign)
      return 1;
    return -1;
  }

  if (xlen < y.len)
    return (x.sign) ? -1 : 1;
  if (xlen > y.len)
    return (x.sign) ? 1 : -1;

  while(xlen-- && (x.digits[xlen] == y.digits[xlen]))
    ;
  if (-1 == xlen)
    return 0;
  return (x.digits[xlen] > y.digits[xlen]) ?
    (x.sign ? 1 : -1) :
    (x.sign ? -1 : 1);
}

function bigint_number(x)
{
  var d = 0.0;
  var i = x.len;
  var ds = x.digits;

  while (i--) {
    d = ds[i] + 65536.0 * d;
  }
  if (!x.sign) d = -d;
  return d;
}
